import pandas as pd
import numpy as np
import os
pd.set_option("display.max_rows", 100)
pd.set_option('display.max_columns', 200)
import warnings
warnings.filterwarnings('ignore')
%config InlineBackend.figure_format = 'svg'
from tqdm import tqdm
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
import matplotlib.gridspec as gridspec
%matplotlib inline
plt.rcParams['figure.figsize'] = [12.0, 4.0]
# plt.rcParams['figure.dpi'] = 80
from sklearn.metrics import f1_score, recall_score, precision_score, classification_report
from sklearn.cluster import DBSCAN
from collections import Counter
from sklearn.decomposition import PCA
from sklearn import manifold
from sklearn.model_selection import KFold
def get_train_sample(X, y, n, bot_rate=0.1):
X_bot = X[y==1].values
X_user = X[y==0].values
if bot_rate<1:
n_bot = int(bot_rate * n)
else:
n_bot = int(bot_rate)
assert n_bot < n
bot_indexes = np.random.choice(np.arange(X_bot.shape[0]), size=n_bot, replace=False)
X_train_bot = X_bot[bot_indexes]
user_indexes = np.random.choice(np.arange(X_user.shape[0]), size=n - n_bot, replace=False)
X_train_user = X_user[user_indexes]
return np.vstack((X_train_bot, X_train_user)), np.hstack((np.ones(n_bot), np.zeros(n - n_bot))).reshape(-1, 1)
def get_random_sample(X, y, n):
choosen_indexes = np.random.choice(X.index, size=n, replace=False)
X_random = X.loc[choosen_indexes]
y_random = y.loc[choosen_indexes].values
return X_random, y_random
def plot_2d_distr_scatter(X_sample, y_sample, labels=None):
label_1 = np.where(y_sample==1)[0]
label_0 = np.where(y_sample==0)[0]
plot_number = 2 if labels!=None else 1
plt.figure(figsize=(12,5))
gs = gridspec.GridSpec(1, plot_number)
ax1 = plt.subplot(gs[0, 0])
ax1.set_title('Реальное распределение ботов')
plt.scatter(X_sample[label_1, 0], X_sample[label_1, 1], 10, c='r', label='bot')
plt.scatter(X_sample[label_0, 0], X_sample[label_0, 1], 2, c='g', label='user')
ax1.legend()
if labels!=None:
ax2 = plt.subplot(gs[0, 1])
label_1 = np.where(labels==1)[0]
label_0 = np.where(labels==0)[0]
ax2.set_title('Распределение меток')
ax2.scatter(X_sample[label_1, 0], X_sample[label_1, 1], 10, c='r', label='label 1')
ax2.scatter(X_sample[label_0, 0], X_sample[label_0, 1], 2, c='g', label='label 0')
ax2.legend()
plt.show()
def plot_3d_distr_scatter(X_sample, y_sample, labels=None):
label_1 = np.where(y_sample==1)[0]
label_0 = np.where(y_sample==0)[0]
plot_number = 2 if labels!=None else 1
fig = plt.figure(figsize=[12,5])
ax1 = fig.add_subplot(1, plot_number, 1, projection='3d')
xs = X_sample[label_1, 0]
ys = X_sample[label_1, 1]
zs = X_sample[label_1, 2]
ax1.scatter(xs, ys, zs, c='r', zdir='x', s=5, label='bot')
ax1.set_title('Реальное распределение ботов')
xs = X_sample[label_0, 0]
ys = X_sample[label_0, 1]
zs = X_sample[label_0, 2]
ax1.scatter(xs, ys, zs, c='g', zdir='x', s=2, label='user')
ax1.legend()
if labels != None:
label_1 = np.where(labels==1)[0]
label_0 = np.where(labels==0)[0]
ax2 = fig.add_subplot(1,2,2, projection='3d')
ax2.set_title('Распределение меток')
xs = X_sample[label_1, 0]
ys = X_sample[label_1, 1]
zs = X_sample[label_1, 2]
ax2.scatter(xs, ys, zs, c='r', zdir='x', s=5, label='label_1')
xs = X_sample[label_0, 0]
ys = X_sample[label_0, 1]
zs = X_sample[label_0, 2]
ax2.scatter(xs, ys, zs, c='g', zdir='x', s=2, label='label_0')
ax2.legend()
plt.show()
Получив все эти хорошие представления пора бы уже приступить к собственно кластеризации. Для этих целей был выбран алгоритм DBSCAN, который строит кластера в зависимости от плотности точек. Такой алгоритм выглядит очень удачным после того, что мы наблюдали на предыдущих картинках. Как водится, алгоритм кластеризации выделяет 2 кластера честных и нечестных транзакций. На самом деле выдаст больше, однако мы будем считать точки самого жирного класстера хорошими, а все остальные - аномальными.
Алгоритм достаточно тяжеловесный, так что так или иначе нам пригодятся и методы понижения размерности и какое-то разбиение всего множества наблюдений.
Помимо него попробую применить One-class svm
В принципе, если прямо гнаться за точностью, можно попробовать разбиения, при которых каждый элемент попадает сразу в несколько выборок, а потом оценить, как часто он попадает в подозрительные на фрод. Или просто по результатам этих кластеризаций отранжировать по подозрительности все точки
X = pd.read_csv('clean_data.csv')
y = pd.read_csv('clean_target.csv', header=None).loc[:, 0]
X.head()
Перед тем как чего-нибудь считать, пропатчим наш Дбскан и помимо правленных меток добавим ему параметр min_bot_num/max_bot_num - минимальное/максимальное число аномалий, которое он должен вернуть.
Добьемся мы этого инкрементным/декрементым изменением параметра eps на некоторое заданное значение до тех пор, пока не выполнится это условие. По идее если заморочиться, то можно и значение этого шага сделать адаптивным, но мы люди простые.
def my_db_scan(X, min_samples, eps, min_bot_num=0, max_bot_num=100000, verbose=True):
visited_eps = set()
while(True):
if eps in visited_eps:
return final_labels
visited_eps.add(eps)
if verbose:
print('Текущий параметр:', eps)
dbs = DBSCAN(min_samples=min_samples, eps=eps, n_jobs=-1)
dbs_labels = dbs.fit_predict(X)
label_counter = Counter(dbs_labels)
main_cluster_label = max(label_counter, key=label_counter.get)
final_labels = dbs_labels.copy()
final_labels[dbs_labels!=main_cluster_label] = 1
final_labels[dbs_labels==main_cluster_label] = 0
finded_bots_num = final_labels.sum()
if verbose:
print('Всего отмечено: {}'.format(finded_bots_num))
if finded_bots_num < min_bot_num:
eps -= 0.1
continue
if finded_bots_num > max_bot_num:
eps += 0.1
continue
if verbose:
print('Всего ботов: {}'.format(y_sample.sum()))
print('Верно отмечено: {}'.format(len([x for x in np.where(
y_sample==1)[0] if x in np.where(final_labels==1)[0]])))
print('Ф-мера: {}'.format(f1_score(y_pred=final_labels, y_true=y_sample)))
print('Полнота: {}'.format(recall_score(y_pred=final_labels, y_true=y_sample)))
print('Точность: {}'.format(precision_score(y_pred=final_labels, y_true=y_sample)))
return final_labels, eps
X_sample, y_sample = get_random_sample(X, y, 20000)
Замечание. metric='canberra' следует брать меньше eps. Также при увеличении числа точек, есть резон уменьшать eps. При увеличении размерности X стоит начать думать об увеличении.
%time y_labels, _ = my_db_scan(X_sample, min_samples=2, eps=6.1, min_bot_num=10, max_bot_num=300)
В принципе получилось достаточно неплохо и считалось не так уж прям медленно.
%time y_labels, _ = my_db_scan(X_sample, min_samples=2, eps=7, min_bot_num=10, max_bot_num=300)
pca = PCA(n_components=2, random_state=42).fit(X)
X_pca = pca.transform(X_sample)
y_labels, _ = my_db_scan(X_pca, min_samples=2, eps=1, min_bot_num=10, max_bot_num=300)
plot_2d_distr_scatter(X_pca, y_sample, y_labels)
Совсем грусть и тоска. Результаты совсем удручающие, но глядя на картинки понимаешь, что ожидать большего и не приходилось.
pca = PCA(n_components=3, random_state=42).fit(X)
X_pca = pca.fit_transform(X_sample)
y_labels, _ = my_db_scan(X_pca, min_samples=1, eps=1, min_bot_num=10, max_bot_num=150)
plot_3d_distr_scatter(X_pca, y_sample, y_labels)
pca = PCA(n_components=3, random_state=42).fit(X)
X_pca = pca.fit_transform(X_sample)
y_labels, _ = my_db_scan(X_pca, min_samples=1, eps=2.3, min_bot_num=10, max_bot_num=150)
plot_3d_distr_scatter(X_pca, y_sample, y_labels)
Отлично, получился относительно неплохой результат, причем достаточно шустро считается. Попробуем разбить всю нашу совокупную выборку на подвыборки, на которой и посчитать аномалии независимо
pca = PCA(n_components=3, random_state=42).fit(X)
kf=KFold(n_splits=18, random_state=42, shuffle=True)
eps = 2.3
res_labels = pd.DataFrame()
for i in tqdm(kf.split(X), total=kf.n_splits):
X_sample = X.loc[i[1]]
y_sample = y[i[1]]
X_pca = pca.fit_transform(X_sample)
y_labels, eps = my_db_scan(X_pca, min_samples=1, eps=2.3, min_bot_num=30, max_bot_num=80, verbose=False)
temp_df = pd.DataFrame(index=i[1], data=y_labels, columns=['random_state_42'])
res_labels = res_labels.append(temp_df)
res_labels.sort_index(inplace=True)
print('Всего ботов: {}'.format(y.sum()))
print('Всего отмечено: {}'.format(res_labels.sum()[0]))
print('Верно отмечено: {}'.format(len([x for x in np.where(
y==1)[0] if x in np.where(res_labels.values==1)[0]])))
print('Ф-мера: {}'.format(f1_score(y_pred=res_labels.values, y_true=y)))
print('Полнота: {}'.format(recall_score(y_pred=res_labels.values, y_true=y)))
print('Точность: {}'.format(precision_score(y_pred=res_labels.values, y_true=y)))
res_labels.to_csv('careful_pca_3.csv')
pca = PCA(n_components=3, random_state=42).fit(X)
kf=KFold(n_splits=18, random_state=42, shuffle=True)
eps = 2
res_labels = pd.DataFrame()
for i in tqdm(kf.split(X), total=kf.n_splits):
X_sample = X.loc[i[1]]
y_sample = y[i[1]]
X_pca = pca.fit_transform(X_sample)
y_labels, eps = my_db_scan(X_pca, min_samples=1, eps=eps, min_bot_num=30, max_bot_num=100, verbose=False)
temp_df = pd.DataFrame(index=i[1], data=y_labels, columns=['random_state_42'])
res_labels = res_labels.append(temp_df)
res_labels.sort_index(inplace=True)
print('Всего ботов: {}'.format(y.sum()))
print('Всего отмечено: {}'.format(res_labels.sum()[0]))
print('Верно отмечено: {}'.format(len([x for x in np.where(
y==1)[0] if x in np.where(res_labels.values==1)[0]])))
print('Ф-мера: {}'.format(f1_score(y_pred=res_labels.values, y_true=y)))
print('Полнота: {}'.format(recall_score(y_pred=res_labels.values, y_true=y)))
print('Точность: {}'.format(precision_score(y_pred=res_labels.values, y_true=y)))
res_labels.to_csv('adaptive_pca_3.csv')
pca = PCA(n_components=3, random_state=42).fit(X)
kf=KFold(n_splits=18, random_state=42, shuffle=True)
eps = 1
res_labels = pd.DataFrame()
for i in tqdm(kf.split(X), total=kf.n_splits):
X_sample = X.loc[i[1]]
y_sample = y[i[1]]
X_pca = pca.fit_transform(X_sample)
y_labels, eps = my_db_scan(X_pca, min_samples=1, eps=eps, min_bot_num=30, max_bot_num=150, verbose=False)
temp_df = pd.DataFrame(index=i[1], data=y_labels, columns=['random_state_42'])
res_labels = res_labels.append(temp_df)
res_labels.sort_index(inplace=True)
print('Всего ботов: {}'.format(y.sum()))
print('Всего отмечено: {}'.format(res_labels.sum()[0]))
print('Верно отмечено: {}'.format(len([x for x in np.where(
y==1)[0] if x in np.where(res_labels.values==1)[0]])))
print('Ф-мера: {}'.format(f1_score(y_pred=res_labels.values, y_true=y)))
print('Полнота: {}'.format(recall_score(y_pred=res_labels.values, y_true=y)))
print('Точность: {}'.format(precision_score(y_pred=res_labels.values, y_true=y)))
res_labels.to_csv('agro_pca_3.csv')
Отмечу, что на разбиение на 30тысяч точек оперативной памяти уже не хватает.
X_sample, y_sample = get_random_sample(X, y, 15000)
pca = PCA(n_components=10, random_state=42).fit(X)
X_pca = pca.fit_transform(X_sample)
y_labels, _ = my_db_scan(X_pca, min_samples=1, eps=2, min_bot_num=10, max_bot_num=150)
pca = PCA(n_components=10, random_state=42).fit(X)
kf=KFold(n_splits=18, random_state=42, shuffle=True)
eps = 3.4
res_labels = pd.DataFrame()
for i in tqdm(kf.split(X), total=kf.n_splits):
X_sample = X.loc[i[1]]
y_sample = y[i[1]]
X_pca = pca.fit_transform(X_sample)
y_labels, eps = my_db_scan(X_pca, min_samples=1, eps=eps, min_bot_num=30, max_bot_num=150, verbose=False)
temp_df = pd.DataFrame(index=i[1], data=y_labels, columns=['random_state_42'])
res_labels = res_labels.append(temp_df)
res_labels.sort_index(inplace=True)
print('Всего ботов: {}'.format(y.sum()))
print('Всего отмечено: {}'.format(res_labels.sum()[0]))
print('Верно отмечено: {}'.format(len([x for x in np.where(
y==1)[0] if x in np.where(res_labels.values==1)[0]])))
print('Ф-мера: {}'.format(f1_score(y_pred=res_labels.values, y_true=y)))
print('Полнота: {}'.format(recall_score(y_pred=res_labels.values, y_true=y)))
print('Точность: {}'.format(precision_score(y_pred=res_labels.values, y_true=y)))
res_labels.to_csv('adaptive_pca_10.csv')
del(X_pca)
X_sample, y_sample = get_random_sample(X, y, 5000)
X_mds = manifold.MDS(n_components=2, n_init=1, n_jobs=-1, max_iter=10).fit_transform(X_sample)
y_labels, _ = my_db_scan(X_mds, min_samples=1, eps=1.2, min_bot_num=10, max_bot_num=50)
plot_2d_distr_scatter(X_mds, y_sample, y_labels)
kf=KFold(n_splits=50, random_state=42, shuffle=True)
eps = 1.2
res_labels = pd.DataFrame()
for i in tqdm(kf.split(X), total=kf.n_splits):
X_sample = X.loc[i[1]]
y_sample = y[i[1]]
X_mds = manifold.MDS(n_components=2, n_init=1, n_jobs=-1, max_iter=10).fit_transform(X_sample)
y_labels, eps = my_db_scan(X_mds, min_samples=1, eps=eps, min_bot_num=30, max_bot_num=150, verbose=False)
temp_df = pd.DataFrame(index=i[1], data=y_labels, columns=['random_state_42'])
res_labels = res_labels.append(temp_df)
res_labels.sort_index(inplace=True)
print('Всего ботов: {}'.format(y.sum()))
print('Всего отмечено: {}'.format(res_labels.sum()[0]))
print('Верно отмечено: {}'.format(len([x for x in np.where(
y==1)[0] if x in np.where(res_labels.values==1)[0]])))
print('Ф-мера: {}'.format(f1_score(y_pred=res_labels.values, y_true=y)))
print('Полнота: {}'.format(recall_score(y_pred=res_labels.values, y_true=y)))
print('Точность: {}'.format(precision_score(y_pred=res_labels.values, y_true=y)))
res_labels.to_csv('adaptive_mds_2.csv')
kf=KFold(n_splits=50, random_state=42, shuffle=True)
eps = 2
res_labels = pd.DataFrame()
for i in tqdm(kf.split(X), total=kf.n_splits):
X_sample = X.loc[i[1]]
y_sample = y[i[1]]
X_mds = manifold.MDS(n_components=2, n_init=1, n_jobs=-1, max_iter=10).fit_transform(X_sample)
y_labels, eps = my_db_scan(X_mds, min_samples=1, eps=eps, min_bot_num=10, max_bot_num=35, verbose=False)
temp_df = pd.DataFrame(index=i[1], data=y_labels, columns=['random_state_42'])
res_labels = res_labels.append(temp_df)
res_labels.sort_index(inplace=True)
print('Всего ботов: {}'.format(y.sum()))
print('Всего отмечено: {}'.format(res_labels.sum()[0]))
print('Верно отмечено: {}'.format(len([x for x in np.where(
y==1)[0] if x in np.where(res_labels.values==1)[0]])))
print('Ф-мера: {}'.format(f1_score(y_pred=res_labels.values, y_true=y)))
print('Полнота: {}'.format(recall_score(y_pred=res_labels.values, y_true=y)))
print('Точность: {}'.format(precision_score(y_pred=res_labels.values, y_true=y)))
res_labels.to_csv('careful_mds_2.csv')
Построим 4 различных разбиений исходного датасета, а затем посмотрим, как часто та или иная транзакция была отмечена как аномальная
for random_state in {12, 15, 20, 24, 36}:
kf=KFold(n_splits=50, random_state=random_state, shuffle=True)
eps = 1.7
res_labels = pd.DataFrame()
for i in tqdm(kf.split(X), total=kf.n_splits):
X_sample = X.loc[i[1]]
y_sample = y[i[1]]
X_mds = manifold.MDS(n_components=2, n_init=1, n_jobs=-1, max_iter=10).fit_transform(X_sample)
y_labels, eps = my_db_scan(X_mds, min_samples=1, eps=eps, min_bot_num=15, max_bot_num=40, verbose=False)
temp_df = pd.DataFrame(index=i[1], data=y_labels, columns=['random_state_{}'.format(random_state)])
res_labels = res_labels.append(temp_df)
res_labels.sort_index(inplace=True)
print('Всего ботов: {}'.format(y.sum()))
print('Всего отмечено: {}'.format(res_labels.sum()[0]))
print('Верно отмечено: {}'.format(len([x for x in np.where(
y==1)[0] if x in np.where(res_labels.values==1)[0]])))
print('Ф-мера: {}'.format(f1_score(y_pred=res_labels.values, y_true=y)))
print('Полнота: {}'.format(recall_score(y_pred=res_labels.values, y_true=y)))
print('Точность: {}'.format(precision_score(y_pred=res_labels.values, y_true=y)))
res_labels.to_csv('mds_2_{}.csv'.format(random_state))
summary = pd.DataFrame()
for random_state in {12, 15, 20, 24, 36}:
summary['rs_{}'.format(random_state)] = pd.read_csv(
'mds_2_{}.csv'.format(random_state), index_col=0).iloc[:, 0]
summary['overall'] = summary.sum(axis=1)
summary.head()
summary.overall.value_counts()
summary_labels = np.array(summary.overall > 4, dtype=np.int8)
print('Всего ботов: {}'.format(y.sum()))
print('Всего отмечено: {}'.format(summary_labels.sum()))
print('Верно отмечено: {}'.format(len([x for x in np.where(
y==1)[0] if x in np.where(summary_labels==1)[0]])))
print('Ф-мера: {}'.format(f1_score(y_pred=summary_labels, y_true=y)))
print('Полнота: {}'.format(recall_score(y_pred=summary_labels, y_true=y)))
print('Точность: {}'.format(precision_score(y_pred=summary_labels, y_true=y)))
X_sample, y_sample = get_random_sample(X, y, 5000)
X_mds = manifold.MDS(n_components=3, n_init=1, n_jobs=-1, max_iter=10).fit_transform(X_sample)
y_labels, _ = my_db_scan(X_mds, min_samples=1, eps=4, min_bot_num=10, max_bot_num=50)
plot_3d_distr_scatter(X_mds, y_sample, y_labels)
kf=KFold(n_splits=50, random_state=42, shuffle=True)
eps = 4
res_labels = pd.DataFrame()
for i in tqdm(kf.split(X), total=kf.n_splits):
X_sample = X.loc[i[1]]
y_sample = y[i[1]]
X_mds = manifold.MDS(n_components=3, n_init=1, n_jobs=-1, max_iter=10).fit_transform(X_sample)
y_labels, eps = my_db_scan(X_mds, min_samples=1, eps=eps, min_bot_num=10, max_bot_num=35, verbose=False)
temp_df = pd.DataFrame(index=i[1], data=y_labels, columns=['random_state_42'])
res_labels = res_labels.append(temp_df)
res_labels.sort_index(inplace=True)
print('Всего ботов: {}'.format(y.sum()))
print('Всего отмечено: {}'.format(res_labels.sum()[0]))
print('Верно отмечено: {}'.format(len([x for x in np.where(
y==1)[0] if x in np.where(res_labels.values==1)[0]])))
print('Ф-мера: {}'.format(f1_score(y_pred=res_labels.values, y_true=y)))
print('Полнота: {}'.format(recall_score(y_pred=res_labels.values, y_true=y)))
print('Точность: {}'.format(precision_score(y_pred=res_labels.values, y_true=y)))
res_labels.to_csv('careful_mds_3.csv')
В целом нельзя сказать, что получились результаты на порядок лучше, чем mds-2. Однако считается довольно шустро.
Попробуем аналогичную суммарную таблицу построить по всем нашим исследованиям.
import os
path_list = [path for path in os.listdir() if 'csv' in path and 'data' not in path and 'target' not in path]
summary = pd.DataFrame()
for i, path in enumerate(path_list):
summary['research_{}'.format(i)] = pd.read_csv(path, index_col=0).iloc[:, 0]
summary['overall'] = summary.sum(axis=1)
summary.overall.value_counts()
summary_labels = np.array(summary.overall > 7, dtype=np.int8)
print('Всего ботов: {}'.format(y.sum()))
print('Всего отмечено: {}'.format(summary_labels.sum()))
print('Верно отмечено: {}'.format(len([x for x in np.where(
y==1)[0] if x in np.where(summary_labels==1)[0]])))
print('Ф-мера: {}'.format(f1_score(y_pred=summary_labels, y_true=y)))
print('Полнота: {}'.format(recall_score(y_pred=summary_labels, y_true=y)))
print('Точность: {}'.format(precision_score(y_pred=summary_labels, y_true=y)))
summary_labels = np.array(summary.overall > 7, dtype=np.int8)
plt.figure(figsize=(12,3))
gs = gridspec.GridSpec(1, 3)
ax1 = plt.subplot(gs[0, 0])
ax1.set_title('Полнота')
summary_list = [np.array(summary.overall > t, dtype=np.int8) for t in range(1, 13)]
recall_by_level = [recall_score(y_pred=elem, y_true=y) for elem in summary_list]
ax1.plot(range(1, 13), recall_by_level)
ax1.set_xticks(range(1, 13))
ax1.set_xlabel('Порог')
ax2 = plt.subplot(gs[0, 1])
ax2.set_title('Точность')
precision_by_level = [precision_score(y_pred=elem, y_true=y) for elem in summary_list]
ax2.plot(range(1, 13), precision_by_level)
ax2.set_xticks(range(1, 13))
ax2.set_xlabel('Порог')
ax3 = plt.subplot(gs[0, 2])
ax3.set_title('Ф-мера')
f1_by_level = [f1_score(y_pred=elem, y_true=y) for elem in summary_list]
ax3.plot(range(1, 13), f1_by_level)
ax3.set_xticks(range(1, 13))
ax3.set_xlabel('Порог')
del(summary_list)
plt.show()
В общем по графикам видно, что 7-8 хорошее значение порога на сумму меток
Буду пробовать на самых удачных конфигурациях, а именно на pca-10 и mds-3
from sklearn.svm import OneClassSVM
kf=KFold(n_splits=5, random_state=42, shuffle=True)
pca = PCA(n_components=10).fit(X)
res_labels = pd.DataFrame()
for i in tqdm(kf.split(X), total=kf.n_splits):
X_sample = X.loc[i[1]]
y_sample = y[i[1]]
X_pca = pca.transform(X_sample)
clf = OneClassSVM(kernel='rbf', random_state=42, nu=0.011, gamma=0.03)
clf.fit(X_pca)
final_labels = (clf.predict(X_pca) + 1) / 2
labels_counter = Counter(final_labels)
if labels_counter[1] > labels_counter[0]:
final_labels = -final_labels + 1
temp_df = pd.DataFrame(index=i[1], data=final_labels, columns=['random_state_42'])
res_labels = res_labels.append(temp_df)
res_labels.sort_index(inplace=True)
print('Всего ботов: {}'.format(y.sum()))
print('Всего отмечено: {}'.format(res_labels.sum()[0]))
print('Верно отмечено: {}'.format(len([x for x in np.where(
y==1)[0] if x in np.where(res_labels.values==1)[0]])))
print('Ф-мера: {}'.format(f1_score(y_pred=res_labels.values, y_true=y)))
print('Полнота: {}'.format(recall_score(y_pred=res_labels.values, y_true=y)))
print('Точность: {}'.format(precision_score(y_pred=res_labels.values, y_true=y)))
res_labels.to_csv('ocsvm_pca_10.csv')
Очень быстро, но тяжело контролировать точность: параметры влияют не так очевидно, как в случае с DBSCAN
from sklearn.svm import OneClassSVM
kf=KFold(n_splits=50, random_state=42, shuffle=True)
res_labels = pd.DataFrame()
for i in tqdm(kf.split(X), total=kf.n_splits):
X_sample = X.loc[i[1]]
y_sample = y[i[1]]
X_mds = manifold.MDS(n_components=3, n_init=1, n_jobs=-1, max_iter=10).fit_transform(X_sample)
clf = OneClassSVM(kernel='rbf', random_state=42, nu=0.012, gamma=0.03)
clf.fit(X_mds)
final_labels = (clf.predict(X_mds) + 1) / 2
labels_counter = Counter(final_labels)
if labels_counter[1] > labels_counter[0]:
final_labels = -final_labels + 1
temp_df = pd.DataFrame(index=i[1], data=final_labels, columns=['random_state_42'])
res_labels = res_labels.append(temp_df)
res_labels.sort_index(inplace=True)
print('Всего ботов: {}'.format(y.sum()))
print('Всего отмечено: {}'.format(res_labels.sum()[0]))
print('Верно отмечено: {}'.format(len([x for x in np.where(
y==1)[0] if x in np.where(res_labels.values==1)[0]])))
print('Ф-мера: {}'.format(f1_score(y_pred=res_labels.values, y_true=y)))
print('Полнота: {}'.format(recall_score(y_pred=res_labels.values, y_true=y)))
print('Точность: {}'.format(precision_score(y_pred=res_labels.values, y_true=y)))
res_labels.to_csv('ocsvm_mds_2.csv')
Проблемы все те же, только теперь еще и считается дольше. В целом возможна лучшая работа алгоритма при более аккуратной настройке параметров, однако как автоматизировать их настройку пока особо идей нет. Возможно при этом получится использовать этот алгоритм как предварительное сито например, так как работает на готовых признаках очень быстро
Ну идея - предсказывать значение USD по остальным колонкам на отдельных выборках. В выборках где получилось относительно небольшое значение функции потерь считаем, что выбросов нет, и соответственно отмечаем те точки. Алгоритм стохастический, к его завершению получаем некоторую маску на точки, которая говорит, является ли та или иная точка outlier-ом.
from sklearn.linear_model import RANSACRegressor, Ridge
base_clf = Ridge(alpha=1)
clf = RANSACRegressor(base_estimator=base_clf, min_samples=0.01, max_trials=10000, stop_n_inliers=280000)
X_prev = X.drop('USD', 1)
y_now = X.USD
clf.fit(X_prev, y_now)
В общем такие подходы я поиспользовал, на каком именно решении следует остановиться и конкретный пул параметров зависит от конкретной постановки задачи, как много ошибок первого и второго рода нам допустимы. В целом, как мне кажется, большой зоопарк методов идет только на пользу, как минимум для возможности оценки ботов по сразу нескольким критериям.
Пока лучшее, что я увидел - это чистый dbscan и dbscan после PCA-10 и MDS-2,3